package br.senai.sc.controller.business;

import java.io.Serializable;
import java.math.BigInteger;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.jboss.seam.international.status.Messages;
import org.jboss.seam.international.status.builder.BundleKey;

import br.senai.sc.model.Cidade;
import br.senai.sc.model.CorpoHidrico;
import br.senai.sc.util.SystemConstants;

/**
 * Regras de negócio da entidade {@link CorpoHidrico}
 * 
 * @author Tiago Wanke Marques &lt;tiago.marques@sc.senai.br&gt;
 */
public class CorpoHidricoBusiness implements Serializable {

	private static final long serialVersionUID = 1L;

	@PersistenceContext
	private EntityManager em;

	@Inject
	private Messages messages;

	@Inject
	private BaciaHidrograficaBusiness baciaHidrograficaBusiness;

	/**
	 * Verifica se {@link CorpoHidrico} existe.
	 * 
	 * @param corpoHidrico
	 * @return True caso exista, false caso contrário.
	 */
	public boolean existe(CorpoHidrico corpoHidrico) {

		if ((corpoHidrico == null)
				|| (corpoHidrico.getIdCorpoHidrico() == null)) {
			return false;
		}

		BigInteger total = (BigInteger) em
				.createNativeQuery(
						"SELECT count(*) FROM corpo_hidrico ch WHERE id_corpo_hidrico = :idCorpoHidrico")
				.setParameter("idCorpoHidrico",
						corpoHidrico.getIdCorpoHidrico()).getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * Verifica se existe {@link CorpoHidrico} com mesmo nome.
	 * 
	 * @param corpoHidrico
	 * @return True caso exista, false caso contrário.
	 */
	public boolean existeComMesmoNome(CorpoHidrico corpoHidrico) {

		if ((corpoHidrico == null) || (corpoHidrico.getNome() == null)) {
			return false;
		}

		BigInteger total = (BigInteger) em
				.createNativeQuery(
						"SELECT count(*) FROM corpo_hidrico ch WHERE nome LIKE :nome")
				.setParameter("nome", corpoHidrico.getNome()).getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * Verifica se existe {@link CorpoHidrico} com mesmo nome, porém com id
	 * diferente.
	 * 
	 * @param corpoHidrico
	 * @return True caso existe, false caso contrário.
	 */
	public boolean existeComMesmoNomeIdDiferente(CorpoHidrico corpoHidrico) {

		if ((corpoHidrico == null) || (corpoHidrico.getNome() == null)
				|| (corpoHidrico.getIdCorpoHidrico() == null)) {
			return false;
		}

		BigInteger total = (BigInteger) em
				.createNativeQuery(
						"SELECT count(*) FROM corpo_hidrico ch WHERE nome LIKE :nome AND id_corpo_hidrico != :idCorpoHidrico")
				.setParameter("nome", corpoHidrico.getNome())
				.setParameter("idCorpoHidrico",
						corpoHidrico.getIdCorpoHidrico()).getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * Verifica se o {@link CorpoHidrico} possui
	 * {@link br.senai.sc.model.PontoColeta}.
	 * 
	 * @param corpoHidrico
	 * @return True caso possua, false caso contrário.
	 */
	public boolean possuiPontoColeta(CorpoHidrico corpoHidrico) {

		if ((corpoHidrico == null)
				|| (corpoHidrico.getIdCorpoHidrico() == null)) {
			return false;
		}

		BigInteger total = (BigInteger) em
				.createNativeQuery(
						"SELECT count(*) FROM ponto_coleta pc WHERE id_corpo_hidrico = :idCorpoHidrico")
				.setParameter("idCorpoHidrico",
						corpoHidrico.getIdCorpoHidrico()).getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * @param corpoHidrico
	 * @param cidade
	 * @return True caso a {@link Cidade} informada esteja no
	 *         {@link CorpoHidrico}, false caso contrário.
	 */
	public boolean possuiCidade(CorpoHidrico corpoHidrico, Cidade cidade) {

		if ((corpoHidrico == null)
				|| (corpoHidrico.getIdCorpoHidrico() == null)
				|| (cidade == null) || (cidade.getIdCidade() == null)) {
			return false;
		}

		BigInteger total = (BigInteger) em
				.createNativeQuery(
						"SELECT count(*) FROM corpo_hidrico_cidade chc WHERE id_corpo_hidrico = :idCorpoHidrico AND id_cidade = :idCidade")
				.setParameter("idCorpoHidrico",
						corpoHidrico.getIdCorpoHidrico())
				.setParameter("idCidade", cidade.getIdCidade())
				.getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * @return True caso exista {@link CorpoHidrico} cadastrado no sistema,
	 *         false caso contrário.
	 */
	public boolean existeCorpoHidrico() {

		BigInteger total = (BigInteger) em.createNativeQuery(
				"SELECT count(*) FROM corpo_hidrico ch").getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * Executa os métodos necessários para verificar se o {@link CorpoHidrico}
	 * pode ser salvo. Caso houver alguma inconsistência adiciona ao atributo de
	 * mensagens da classe.
	 * 
	 * @param corpoHidrico
	 * @return True caso a {@link CorpoHidrico} pode ser salvo, false caso
	 *         contrário.
	 */
	public boolean podeSalvar(CorpoHidrico corpoHidrico) {

		messages.clear();

		if (!baciaHidrograficaBusiness.existe(corpoHidrico
				.getBaciaHidrografica())) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"corposHidricos.validacao.baciaInexistente"));
		}

		if (existeComMesmoNome(corpoHidrico)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"corposHidricos.validacao.comMesmoNome"));
		}

		for (Cidade cidade : corpoHidrico.getCidades()) {
			if (!baciaHidrograficaBusiness.possuiCidade(
					corpoHidrico.getBaciaHidrografica(), cidade)) {
				messages.error(new BundleKey(
						SystemConstants.PATH_TO_APPLICATION_MESSAGES,
						"corposHidricos.validacao.cidadeInexistente"));
			}
		}

		return messages.isEmpty();
	}

	/**
	 * Executa os métodos necessários para verificar se o {@link CorpoHidrico}
	 * pode ser editado. Caso houver alguma inconsistência adiciona ao atributo
	 * de mensagens da classe.
	 * 
	 * @param corpoHidrico
	 * @return True caso pode ser editado, false caso contrário.
	 */
	public boolean podeEditar(CorpoHidrico corpoHidrico) {

		messages.clear();

		if (!existe(corpoHidrico)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"corposHidricos.validacao.corpoHidricoInexistente"));
		}

		if (!baciaHidrograficaBusiness.existe(corpoHidrico
				.getBaciaHidrografica())) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"corposHidricos.validacao.baciaInexistente"));
		}

		if (existeComMesmoNomeIdDiferente(corpoHidrico)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"corposHidricos.validacao.comMesmoNome"));
		}

		for (Cidade cidade : corpoHidrico.getCidades()) {
			if (!baciaHidrograficaBusiness.possuiCidade(
					corpoHidrico.getBaciaHidrografica(), cidade)) {
				messages.error(new BundleKey(
						SystemConstants.PATH_TO_APPLICATION_MESSAGES,
						"corposHidricos.validacao.cidadeInexistente"));
			}
		}
		return messages.isEmpty();
	}

	/**
	 * Executa os métodos necessários para verificar se {@link CorpoHidrico}
	 * pode ser removido. Caso houver alguma inconsistência adiciona ao atributo
	 * de mensagens da classe.
	 * 
	 * @param corpoHidrico
	 * @return True caso pode ser removido, false caso contrário.
	 */
	public boolean podeRemover(CorpoHidrico corpoHidrico) {

		messages.clear();

		if (!existe(corpoHidrico)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"corposHidricos.validacao.corpoHidricoInexistente"));
		}

		if (possuiPontoColeta(corpoHidrico)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"corposHidricos.validacao.possuiPontoColeta"));
		}

		return messages.isEmpty();
	}

}
